/* Box blur (simple smooth) problem.
 * Felipe Osorio Thome 7696409 <f.o.thome@gmail.com>, 2014. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "pbmpak.h"

#define STD_DIM_KERNEL 5

FILE *xfopen(char *, char *);
void *xcalloc(unsigned long int, int);
int *box_blur(int, int, int *);
int box_blur_aux(int, int, int, int, int *);

typedef struct
{
    int xsize;
    int ysize;
    int maxrgb;
} IMG_HEADER;

typedef struct
{
    int *rarray;
    int *garray;
    int *barray;
} IMG_DATA;

int dim_kernel;
int half;

int main(int argc, char *argv[])
{
    FILE *img = NULL;
    IMG_HEADER header;
    IMG_DATA datain, dataout;
    char filename_in[36], filename_out[36];
    int size = 0;

    memset(&header, 0, sizeof(IMG_HEADER));
    memset(&datain, 0, sizeof(IMG_DATA));

    if(argc > 3)
    {
        strcpy(filename_in, argv[1]);
        strcpy(filename_out, argv[2]);

        dim_kernel = atoi(argv[3]);
        half = dim_kernel / 2;
    }
    else
    {
        strcpy(filename_in, "in.ppm");
        strcpy(filename_out, "out.ppm");

        dim_kernel = STD_DIM_KERNEL;
        half = dim_kernel / 2;
    }

    img = xfopen(filename_in, "r");

    ppma_read_header(img, &(header.xsize), &(header.ysize), &(header.maxrgb));

    size = header.xsize * header.ysize;
    datain.rarray = xcalloc(size, sizeof(int));
    datain.garray = xcalloc(size, sizeof(int));
    datain.barray = xcalloc(size, sizeof(int));

    ppma_read_data(img, header.xsize, header.ysize, datain.rarray, datain.garray, datain.barray);

    dataout.rarray = box_blur(header.xsize, header.ysize, datain.rarray);
    dataout.garray = box_blur(header.xsize, header.ysize, datain.garray);
    dataout.barray = box_blur(header.xsize, header.ysize, datain.barray);

    ppma_write(filename_out, header.xsize, header.ysize, header.maxrgb,
               dataout.rarray, dataout.garray, dataout.barray);

    fclose(img);

    free(datain.rarray);
    free(datain.garray);
    free(datain.barray);

    free(dataout.rarray);
    free(dataout.garray);
    free(dataout.barray);

    return 0;
}

int *box_blur(int xsize, int ysize, int *array)
{
    int *result = NULL;
    int i, j;

    result = xcalloc(xsize * ysize, sizeof(int));

    for(i = 0; i < ysize; i++)
    {
        for(j = 0; j < xsize; j++)
        {
            result[i * xsize + j] = box_blur_aux(i, j, xsize, ysize, array);
        }
    }

    return result;
}

int box_blur_aux(int row, int col, int xsize, int ysize, int *array)
{
    int i, j;
    int accumulation = 0, average = 0;

    for(i = row - half; i < (row - half) + dim_kernel; i++)
    {
        for(j = col - half; j < (col - half) + dim_kernel; j++)
        {
            if(i < 0 || i >= ysize) break;
            if(j < 0 || j >= xsize) continue;

            accumulation += array[i * xsize + j];
        }
    }

    average = accumulation / (dim_kernel * dim_kernel);

    return average;
}

FILE *xfopen(char *filename, char *mode)
{
    FILE *ptr = NULL;

    ptr = fopen(filename, mode);

    if(!ptr)
    {
        fprintf(stderr, "%s: %d: %s\n", __FILE__, __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    return ptr;
}

void *xcalloc(unsigned long int num, int size)
{
    void *ptr = NULL;

    ptr = calloc(num, size);

    if(!ptr)
    {
        fprintf(stderr, "%s: %d: %s\n", __FILE__, __LINE__, strerror(errno));
        exit(EXIT_FAILURE);
    }

    return ptr;
}
